﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Mono.CSharp;
using Mono.CSharp.yyParser;
using System.CodeDom;
using System.CodeDom.Compiler;
namespace Smark.Data.InterfaceToModelGenerator
{
    public class GenerateCode
    {

        CodeCompileUnit mEntityUnit = new CodeCompileUnit();
        string mTableName;
        string mClassName;
        public GenerateCode()
        {



        }
        public System.IO.Stream Builder(System.IO.Stream stream)
        {
            System.IO.StringWriter sw = new System.IO.StringWriter();
            Console.SetOut(sw);
            Console.SetError(sw);
            CSharpParser p = new CSharpParser(null, stream, null);
            System.IO.Stream result = new System.IO.MemoryStream();
            System.IO.StreamWriter writer = new System.IO.StreamWriter(result, Encoding.UTF8);
            int errno = p.parse();
            if (errno > 0)
            {
                writer.Write(sw.ToString());
            }
            CodeCompileUnit unit = p.Builder.CurrCompileUnit;

            if (unit != null)
            {
                try
                {
                    Microsoft.CSharp.CSharpCodeProvider csp = new Microsoft.CSharp.CSharpCodeProvider();
                    foreach (string assembly in unit.ReferencedAssemblies)
                    {
                        mEntityUnit.ReferencedAssemblies.Add(assembly);
                    }
                    
                    foreach (CodeNamespace cn in unit.Namespaces)
                    {
                        BuilderNameSpace(cn);
                    }
                    csp.GenerateCodeFromCompileUnit(mEntityUnit, writer, new CodeGeneratorOptions());
                    writer.Flush();
                }
                catch (Exception e_)
                {
                    writer.WriteLine(e_.Message + e_.StackTrace);
                }
            }
            return result;

        }
        private void BuilderNameSpace(CodeNamespace cn)
        {
            CodeNamespace samples = new CodeNamespace(cn.Name);
            foreach (CodeNamespaceImport item in cn.Imports)
            {
                samples.Imports.Add(item);
            }
            foreach (CodeTypeDeclaration item in cn.Types)
            {
                BuilderType(samples, item);
            }
            mEntityUnit.Namespaces.Add(samples);

        }
        private void BuilderType(CodeNamespace cns, CodeTypeDeclaration type)
        {
            if (type.IsInterface)
            {
                mClassName = type.Name.Substring(1, type.Name.Length - 1);
                CodeTypeDeclaration entity = new CodeTypeDeclaration(mClassName);
                 CodeCommentStatement comm;
                cns.Types.Add(entity);
             
              
                foreach (CodeCommentStatement item in type.Comments)
                {
                    
                    if (item.Comment.Text.EndsWith("\r") || item.Comment.Text.EndsWith("\r\n"))
                    {
                        comm = new CodeCommentStatement(
                            item.Comment.Text.Replace("\r", "").Replace("\n", "").TrimStart(' '));
                        comm.Comment.DocComment = true;
                        entity.Comments.Add(comm);
                        if (comm.Comment.Text == "<summary>")
                        {
                            comm = new CodeCommentStatement("Smark.Data Generator Copyright © FanJianHan 2010-2012");
                            comm.Comment.DocComment = true;
                            entity.Comments.Add(comm);
                            comm = new CodeCommentStatement("website:http://www.ikende.com");
                            comm.Comment.DocComment = true;
                            entity.Comments.Add(comm);
                        }
                    }
                    else
                    {
                        entity.Comments.Add(item);
                    }
                }
                entity.BaseTypes.Add(new CodeTypeReference("Smark.Data.Mappings.DataObject"));
                entity.BaseTypes.Add(new CodeTypeReference(type.Name));
                entity.Attributes = MemberAttributes.Public;
                entity.IsPartial = true;
                entity.CustomAttributes.Add(new CodeAttributeDeclaration("Serializable"));
                entity.IsClass = true;
                foreach (CodeAttributeDeclaration aitem in type.CustomAttributes)
                {
                    entity.CustomAttributes.Add(aitem);
                    if (aitem.Name.ToLower() == "table")
                    {
                        if (aitem.Arguments.Count > 0)
                        {
                            CodePrimitiveExpression pe = aitem.Arguments[0].Value as CodePrimitiveExpression;
                            if (pe != null)
                            {
                                mTableName = pe.Value.ToString();
                            }
                            else
                            {
                                mTableName = mClassName;

                            }
                        }
                        else
                        {
                            mTableName = mClassName;
                        }
                    }

                }
                foreach (CodeTypeMember mitem in type.Members)
                {
                    CodeMemberProperty p = mitem as CodeMemberProperty;
                    if (p != null)
                    {
                        BuilderProperty(entity, p);
                    }
                }


            }
        }
        private void BuilderProperty(CodeTypeDeclaration entity, CodeMemberProperty property)
        {
            CodeMemberField field = new CodeMemberField(property.Type, "m" + property.Name);
            CodeMemberField fieldinfo = new CodeMemberField();
            field.Attributes = MemberAttributes.Private;
            entity.Members.Add(field);
            CodeMemberProperty mp = new CodeMemberProperty();
            foreach (CodeCommentStatement item in property.Comments)
            {

                if (item.Comment.Text.EndsWith("\r") || item.Comment.Text.EndsWith("\r\n"))
                {
                    CodeCommentStatement comm = new CodeCommentStatement(
                        item.Comment.Text.Replace("\r", "").Replace("\n", "").TrimStart(' '));
                    comm.Comment.DocComment = true;
                    mp.Comments.Add(comm);
                    fieldinfo.Comments.Add(comm);
                }
                else
                {
                    mp.Comments.Add(item);
                    fieldinfo.Comments.Add(item);
                }
            }
            mp.Name = property.Name.Substring(0, 1).ToUpper() + property.Name.Substring(1, property.Name.Length - 1);
            string columnName = null;
            foreach (CodeAttributeDeclaration aitem in property.CustomAttributes)
            {
                mp.CustomAttributes.Add(aitem);
                if (aitem.Name.ToLower() == "id" || aitem.Name.ToLower() == "column")
                {
                    if (aitem.Arguments.Count > 0)
                    {
                        CodePrimitiveExpression pe = aitem.Arguments[0].Value as CodePrimitiveExpression;
                        if (pe != null)
                        {
                            columnName = pe.Value.ToString();
                        }
                        else
                        {
                            columnName = property.Name;

                        }
                    }
                    else
                    {
                        columnName = property.Name;
                    }
                }
            }
            mp.Attributes = MemberAttributes.Public;
            mp.Type = property.Type;
            entity.Members.Add(mp);
            mp.GetStatements.Add(
                new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name)));

            mp.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name),
                                    new CodePropertySetValueReferenceExpression()));
            mp.SetStatements.Add(
                new CodeMethodInvokeExpression(
                    new CodeMethodReferenceExpression(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "EntityState"), "FieldChange"), new CodePrimitiveExpression(property.Name)));

           
            fieldinfo.Type = new CodeTypeReference("Smark.Data.FieldInfo");
            fieldinfo.Name = property.Name.Substring(0, 1).ToLower() + property.Name.Substring(1, property.Name.Length - 1);
            fieldinfo.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            entity.Members.Add(fieldinfo);
            fieldinfo.InitExpression = new CodeObjectCreateExpression(
                new CodeTypeReference("Smark.Data.FieldInfo"), new CodePrimitiveExpression(mTableName), new CodePrimitiveExpression(columnName));


        }


    }
}
